/**
 * 名字标签挂件：显示标题和文字
 * 
 * 有启动和结束动画
 * 
 * 注意: 因为是挂件, 所以没有 sendStatus 这个方法.
 * 该挂件的 Pendant 实例需要调用 sendStatus 方法, 因此需要上层设置 sendStatus 方法
 */

/**
 * public 创建 Pendant 挂件方法
 */
function createPendant(context) {
  let t = new TitleAndContent(context);
  let p = new context.Pendant({
		require : ['zrender'],
		init : (libs, options) => t.init(libs, options),
		destroy : () => t.destroy(),
  });
  t.finished = () => p.finished();
  return p;
};

let zrender;

/**
 * 上层的 Scene 会注入 finished 方法.
 */
class TitleAndContent {

  /**
   * @type {number} 
   * 阶段
   * 0 - 动画启动阶段
   * 1 - 动画中间阶段
   * 2 - 动画结束阶段
   * 3 - 全部动画结束
   */
  phase = 0;
  /** @type {number} */
  intervalHandler = null;
  /** @type {number} init() 调用完的时间 */
  baseTime = 0;

  zr = null;
  context = null;

  layout = {
    /** 标题位置：左间距 */
    titleOffsetX : 50,
    /** 分割线终点位置: x */
    splitTargetX : 0,
    /** 文本高度 */
    textHeight : 0,
    /** 内容部分需要滚动吗 */
    contentNeedRoll : false,

    canvasWidth : 0,
    canvasHeight : 0,
  };

  sprites = {
    /** 标题文本, Text */
    title : null,
    /** 内容文本, Text */
    content : null,
    /** 分割线, Polyline */
    split : null,

    /** 裁剪标题的, Polyline */
    clipTitle : null,
    /** 裁剪内容的, Polyline */
    clipContent : null,
  };

  props = {
    title : '这是一个标题',
    content : '想说啥呢',
    /** @type {number} 毫秒, 这个是 动画中间阶段 的时间，所有时间加起来是 duration + 1000 */
    duration : 5000,
    el : 'main',

    /** 启动阶段用时 */
    initPhaseDuration : 1000,
    /** 结束阶段用时 */
    destroyPhaseDuration : 1000,
  };

  constructor(context) {
    this.context = context;
  };

  init(libs, args) {
    if (args) {
      Object.assign(this.props, args);
    }

    zrender = libs.zrender;
    let zr = this.zr = zrender.init(document.getElementById(this.props.el));

    this.layout.canvasWidth = zr.getWidth();
    this.layout.canvasHeight = zr.getHeight();

    this.createSprites();

    this.intervalHandler = setInterval(() => this.tick(), 1000 / 60);
    this.baseTime = new Date();
  };

  destroy() {
    this.sprites.title = null;
    this.sprites.content = null;
    this.sprites.split = null;
    this.sprites.clipTitle = null;
    this.sprites.clipContent = null;

    clearInterval(this.intervalHandler);
    zrender.dispose(this.zr);
  };

  tick() {
    const {split, clipTitle, content, clipContent} = this.sprites;
    const {titleOffsetX, textHeight, contentNeedRoll} = this.layout;
    let vSplitX = split.position[0];

    if (this.phase == 0 || this.phase == 2) { // 启动阶段 结束阶段
      clipTitle.attr({
        shape: {
          points: [
            [- titleOffsetX, - textHeight / 2],  // 左上角
            [vSplitX + 5 - titleOffsetX, - textHeight / 2], // 右上角
            [vSplitX - 10 - titleOffsetX, textHeight / 2],  // 右下角
            [- titleOffsetX, textHeight / 2]  // 左下角
          ]
        }
      });
    } else if (this.phase == 1) {
      if (new Date() - this.baseTime > this.props.duration + this.props.initPhaseDuration) {
        this.changePhase();
      }
    }

    // 所有阶段的, clipContent
    if (contentNeedRoll) {
      let contentBox = content.getBoundingRect();
      let contentX = content.position[0] + contentBox.x;
      let contentWidth = contentBox.width;

      clipContent.attr({
        shape: {
          points: [
            [vSplitX + 10 - contentX, -textHeight / 2],  // 左上角
            [contentWidth + 10, -textHeight / 2], // 右上角
            [contentWidth + 10, textHeight / 2],  // 右下角
            [vSplitX - 5 - contentX, textHeight / 2]  // 左下角
          ]
        }
      });
    }
  };
  
  /* **********
   * 动画绘制 *
   ********** */

  createSprites() {
    // 水平基准线
    const {initPhaseDuration, destroyPhaseDuration, duration} = this.props;
    const {canvasHeight, canvasWidth} = this.layout;
    var y = canvasHeight / 2;

    this.zr.add(this.sprites.title = new zrender.Text({
      style: {
        text: this.props.title,
        textAlign: 'left',
        fontWeight: 'bold',
        textVerticalAlign: 'center',
        fontSize: 32,
        fontFamily: 'Microsoft YaHei UI',
        textFill: '#FFF',
        textStroke: '#0008',
        textStrokeWidth: 8,
        fontWeight: 900,
        opacity: 1
      },
      position: [50, y]
    }));
    // 标题宽
    let vTitleWidth = this.sprites.title.getBoundingRect().width;

    this.zr.add(this.sprites.content = new zrender.Text({
      style: {
        text: this.props.content,
        textAlign: 'left',
        fontWeight: 'bold',
        textVerticalAlign: 'center',
        fontSize: 32,
        fontFamily: 'Microsoft YaHei UI',
        textFill: '#FFF',
        textShadowBlur: 0,
        textShadowColor: '#000',
        textShadowOffsetX : 2,
        textShadowOffsetY : 2,
        opacity: 0
      },
      position: [100, y]
    }));
    // 内容宽
    let vContentWidth = this.sprites.content.getBoundingRect().width;

    // 文本高 （取标题高和内容高中较大者）
    let textHeight = this.layout.textHeight =
      Math.max(this.sprites.title.getBoundingRect().height, this.sprites.content.getBoundingRect().height);

    // 分割线
    this.zr.add(this.sprites.split = new zrender.Polyline({
      shape: {
        points: [
          [5, -textHeight / 2],  // 左上角
          [10, -textHeight / 2], // 右上角
          [-5, textHeight / 2],  // 右下角
          [-10, textHeight / 2]  // 左下角
        ]
      },
      style: {
        fill: '#CCC',
        stroke: 'none',
        opacity: 0
      },
      position: [0, y]
    }));

    // 总长 [标题宽 + 50 + 内容宽]
    let vAllWidth = vTitleWidth + 50 + vContentWidth;
    if (vAllWidth <= canvasWidth - 100) { // 不算入: [左边距:50 + 右边距:50]
      /*
        * 总长足够长，需要：
        * 标题和内容往中间靠一点，内容不需要滚动
        */
      var titleX = (canvasWidth - vAllWidth) / 2;
      this.layout.titleOffsetX = titleX;
      this.layout.splitTargetX = titleX + vTitleWidth + 25;

      // 标题
      this.sprites.title.attr({
        position: [titleX, y]
      });
      this.sprites.title.setClipPath(this.sprites.clipTitle = new zrender.Polyline({
        shape: {
          points: [
            [0, 0],  // 左上角
            [5, 0], // 右上角
            [-15, textHeight],  // 右下角
            [0, textHeight]  // 左下角
          ]
        },
        style: {
          opacity: 1
        }
      }));

      // 内容
      this.sprites.content.attr({
        position: [this.layout.splitTargetX + 125, y]
      });
      // 不需要内容分割
    } else {
      // 如果文本太长了怎么办？
      /*
       * 总长不足够长，需要：
       * 标题离左边边框有 50 边距；（默认位置，不需要移动）
       * 内容需要滚动
       */
      this.layout.contentNeedRoll = true;
      let vTitleX = this.layout.titleOffsetX;
      this.layout.splitTargetX = vTitleX + vTitleWidth + 25;
      
      // 标题
      this.sprites.title.setClipPath(this.sprites.clipTitle = new zrender.Polyline({
        shape: {
          points: [
            [0, 0],  // 左上角
            [5, 0], // 右上角
            [-15, textHeight],  // 右下角
            [0, textHeight]  // 左下角
          ]
        }
      }));
    }

    // ---- 后面动画部分 ----

    // zrender.Animatable.animateTo(target, time, delay, easing, callback, forceAnimate)
    // 分割线右移动画
    this.sprites.split.animateTo({
      position: [this.layout.splitTargetX, y],
      style : {
        opacity : 1,
      }
    }, initPhaseDuration, 0, 'cubicOut', () => this.changePhase());

    let endX;
    if (this.layout.contentNeedRoll) {
      // 在阶段 1 和 2 的分界中心到达的位置
      let endX0 = canvasWidth - 150 - vContentWidth;
      // 阶段 1 中，这个滚动条走过的距离
      let diff1 = this.layout.splitTargetX + 25 - endX0;
      // 阶段 0-2 中，这个滚动条走过的距离
      let diff = diff1 * (initPhaseDuration + destroyPhaseDuration + duration) / duration;
      endX = this.layout.splitTargetX + 25 - diff;

      // 内容启动位置修改
      // 在阶段 0 和 1 的分界中心到达的位置: x = this.layout.splitTargetX + 25
      // 阶段 0 中，这个滚动条走过的距离:
      let diff0 = diff1 * initPhaseDuration / duration;
      this.sprites.content.attr({
        position: [this.layout.splitTargetX + 25 + diff0, y]
      });
      this.sprites.clipContent = new zrender.Polyline({
        shape: {
          points: [
            [0, 0],  // 左上角
            [canvasWidth, 0], // 右上角
            [canvasWidth, textHeight],  // 右下角
            [0, textHeight]  // 左下角
          ]
        }
      });
      this.sprites.content.setClipPath(this.sprites.clipContent);
    } else {
      endX = this.layout.splitTargetX + 25;
    }

    this.sprites.content.animate('style', false)
      .when(initPhaseDuration, {
        opacity : 1,
        textShadowBlur: 10
      })
      .start();
      
    this.sprites.content.animate('position', false)
      .when((this.layout.contentNeedRoll) ? initPhaseDuration + destroyPhaseDuration + duration : initPhaseDuration,
      [endX, y])
      .start();
  };

  changePhase() {
    let {canvasHeight} = this.layout;
    let {content, split} = this.sprites;
    let {destroyPhaseDuration} = this.props;

    if (this.phase == 0) {
      this.phase = 1;
    } else if (this.phase == 1) {
      this.phase = 2;
      content.animate('style', false)
        .when(destroyPhaseDuration, {
          opacity : 0,
          textShadowBlur: 0
        })
        .start();

      split.animateTo({
        position: [0, canvasHeight / 2],
        style : {
          opacity : 0,
        }
      }, destroyPhaseDuration, 0, 'cubicIn', () => this.changePhase());
    } else {
      this.phase = 3;
      if (this.finished) {
        this.finished();
      }
    }
  }
};

  // 对外开放哪些方法
export default createPendant;
